package com.yxh.www.apiscan.conf;

import com.alibaba.fastjson.JSONObject;
import com.yxh.www.apiscan.annotation.VerifyPermission;
import com.yxh.www.apiscan.entity.ServerApi;
import com.yxh.www.apiscan.entity.ServerApiKey;
import com.yxh.www.apiscan.properties.ServerApiScanProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.regex.Pattern;

/**
 * <p>
 *
 * </p>
 *
 * @author yangxiaohui
 * @since 2020/5/9
 */
@SuppressWarnings({"all"})
@Slf4j
@Configuration
@EnableConfigurationProperties(ServerApiScanProperties.class)
@ConditionalOnProperty(
        prefix = "server.api",
        name = "enable",
        havingValue = "true"
)
public class ServerApiScanConfig {

    private final ServerApiScanProperties serverApiScanProperties;

    public ServerApiScanConfig(ServerApiScanProperties serverApiScanProperties) {
        this.serverApiScanProperties = serverApiScanProperties;
    }

    @Bean
    public List<ServerApi> getServerApis(WebApplicationContext applicationContext, RedisTemplate<String, Object> redisTemplate) {
        List<ServerApi> serverApis = new ArrayList<>();
        String apisKey="APIS_"+serverApiScanProperties.getServerId();
        RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
        // 获取url与类和方法的对应信息
        Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
        // API版本
        TreeSet<String> apiVersionSet=new TreeSet<>();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : map.entrySet()) {
            // 包路径
            String classPackage = entry.getValue().getBeanType().getName();
            // 判断路径是否需要加载该接口
            if (Pattern.matches(this.buildRegexPackage(), classPackage)) {
                // 构建实体
                ServerApi serverApi = new ServerApi();
                // 方法唯一标识，用来生成Hash判断是否被更新
                StringBuffer methodKey=new StringBuffer();
                // 方法
                Method method = entry.getValue().getMethod();
                // 获取方法请求类型
                Object[] methodTypes = entry.getKey().getMethodsCondition().getMethods().toArray();
                // 获取方法路径
                Object[] methodPaths = entry.getKey().getPatternsCondition().getPatterns().toArray();
                methodKey.append(serverApiScanProperties.getServerId() + methodPaths[0].toString()+methodTypes[0].toString());
                // 判断是否包含权限标识注解
                if (method.isAnnotationPresent(VerifyPermission.class)) {
                    VerifyPermission verifyPermission = method.getAnnotation(VerifyPermission.class);
                    if (null != verifyPermission && verifyPermission.keys().length > 0) {
                        serverApi.setPermissionKeys(StringUtils.join(verifyPermission.keys(), ","));
                        methodKey.append(StringUtils.join(verifyPermission.keys()));

                    }
                    if (StringUtils.isNotBlank(verifyPermission.name())){
                        methodKey.append(verifyPermission.name());
                    }
                }
                serverApi.setApiName(methodPaths[0].toString());
                serverApi.setApiPath("/" + serverApiScanProperties.getServerId() + methodPaths[0].toString());
                serverApi.setApiType(methodTypes[0].toString());
                serverApi.setServerId(serverApiScanProperties.getServerId());
                serverApi.setServerName(serverApiScanProperties.getServerId());
                serverApis.add(serverApi);
                apiVersionSet.add(methodKey.toString());
                log.info("服务API：{} ", JSONObject.toJSONString(serverApi));
            }
        }
        // 放入Redis数据
        if (serverApis.size()>0) {
            // 删除旧数据
            redisTemplate.delete(apisKey);
            // 放入新数据
            redisTemplate.opsForList().leftPushAll(apisKey, serverApis.toArray());
            // 把Key放入集合，表示Api发生了更改
            ServerApiKey serverApiKey=new ServerApiKey(DigestUtils.md5Hex(apiVersionSet.toString()),apisKey);
            redisTemplate.opsForList().rightPush("ServerApiKeys",serverApiKey);
        }
        return serverApis;
    }

    private String buildRegexPackage() {
        return serverApiScanProperties.getScanPackage().replace("**", "[\\w]*") + ".[\\w]*";
    }
}
